<?php

declare(strict_types=1);

namespace App\CustHub\Mapper;

use App\CustHub\Model\ChRoom;
use Hyperf\Cache\Annotation\Cacheable;
use Hyperf\Config\Annotation\Value;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Collection;
use Hyperf\Database\Model\Model;
use Mine\Abstracts\AbstractMapper;
use Mine\MineModel;
use function Hyperf\Collection\data_get;

class ChRoomMapper extends AbstractMapper
{
    #[Value('cache.default.prefix')]
    protected string $prefix;

    public function assignModel()
    {
        $this->model = ChRoom::class;
    }

    /**
     * 创建群
     * @param array $data
     * @return ChRoom|Model|string
     */
    public function create(array $data): ChRoom|Model|string
    {
        $this->filterExecuteAttributes($data, $this->getModel()->incrementing);
        $model = $this->model::create($data);
        return $model;
    }

    /**
     * 根据id查询群信息
     * @param mixed $id
     * @return ChRoom|Model|string|Builder|null
     */
    #[Cacheable(prefix: 'ch_room_info',value: 'id_#{id}',ttl: 15 * 60)]
    public function findById(mixed $id): ChRoom|Model|string|Builder|null
    {
        return $this->model::query()
            ->where('id',$id)
            ->first();
    }

    /**
     * 根据群wxid查询群信息
     * @param mixed $roomWxid
     * @param int $mainUid
     * @return ChRoom|Model|string|Builder|null
     */
    #[Cacheable(prefix: 'ch_room_info_wxid',value: 'wxid_#{roomWxid}_#{mainUid}',ttl: 15 * 60)]
    public function findByRoomWxid(mixed $roomWxid,int $mainUid): ChRoom|Model|string|Builder|null
    {
        return $this->model::query()
            ->where('main_uid',$mainUid)
            ->where('room_wxid',$roomWxid)
            ->first();
    }

    #[Cacheable(prefix: 'ch_robot_room_info',value: 'wxid_#{roomWxid}_#{robotId}',ttl: 15 * 60)]
    public function findByRobotIdAndRoomWxid(mixed $roomWxid,int $robotId): ChRoom|Model|string|Builder|null
    {
        return $this->model::query()
            ->where('room_wxid',$roomWxid)
            ->whereHas('robots',function ($query) use ($robotId){
                $query->where('robot_id',$robotId);
            })
            ->first();
    }

    /**
     * 减少群成员数量
     * @param mixed $roomWxid
     * @param int $mainUid
     * @param int $count
     * @return int
     */
    public function decrementRoomMemberCount(int $mainUid,mixed $roomWxid,int $count)
    {
        return $this->model::query()
            ->where('main_uid',$mainUid)
            ->where('room_wxid',$roomWxid)
            ->decrement('room_member_count',$count);
    }

    public function clearCacheById(mixed $id): void
    {
        $redis = redis();
        $keys[] = $this->prefix . 'ch_room_info:id_'.$id;
        $redis->del($keys);
    }

    public function clearCacheByRoomWxId(mixed $roomWxid,int $mainUid): void
    {
        $redis = redis();
        $keys[] = $this->prefix . 'ch_room_info_wxid:wxid_'.$roomWxid.'_'.$mainUid;
        $redis->del($keys);
    }

    public function clearCacheByRobotIdAndRoomWxid(mixed $roomWxid,int $robotId): void
    {
        $redis = redis();
        $keys[] = $this->prefix . 'ch_robot_room_info:wxid_'.$roomWxid.'_'.$robotId;
        $redis->del($keys);
    }

    /**
     * 根据机器人id查询群列表
     * @param mixed $robotId
     * @return array|Collection|Model[]
     */
    public function getRobotRoomList(mixed $robotId): Collection|array
    {
        return $this->model::query()
            ->select(['id','room_wxid','room_name'])
            ->whereHas('robots',function ($query) use ($robotId){
                $query->where('robot_id',$robotId);
            })
            ->get();
    }

    public function getRoomPageList(array $data): array
    {
        $pageSize = $data['page_size'] ?? MineModel::PAGE_SIZE;
        $query =  $this->model::query()
            ->select(['id','room_name','room_announcement','room_wxid','room_avatar_url','room_member_count','room_is_forbid_change_name','room_is_invite_confirmation','create_time','room_owner_name','room_owner_time','is_saved'])
            ->with([
                'robots' => function($query){
                    $query->select(['id','nickname','avatar','corp_name','login_status']);
                },
                'roomTags' => function ($query) {
                    $query->select(['id','name']);
                },'groups'=>function ($query) {
                    $query->select(['id','name']);
                }
            ]);
        $data =  $this->handleSearch($query, $data)->orderBy('create_time','desc')->paginate((int)$pageSize);
        $data->getCollection()->transform(function ($item){
           $item->create_time_text = datetime($item->create_time);

           $robots = $item->robots;
           $saved = [];
           $totalCount = 0;
           $savedCount = 0;
           $mdrTotalCount = 0;
           $mdr = [];
           $mdrCount = 0;
           foreach ($robots as $robot){
               $totalCount++;
               $mdrTotalCount++;
                if($robot->pivot->is_saved == 1){
                    $saved[] = $robot->nickname.'已保存';
                    $mdr[] = $robot->nickname.'已设置';
                    $savedCount++;
                    $mdrCount++;
                }else{
                    $saved[] = $robot->nickname.'未保存';
                    $mdr[] = $robot->nickname.'未设置';
                }
           }

           $item->saved = $saved;
           $item->total_count = $totalCount;
           $item->saved_count = $savedCount;
           
           $item->mdr = $mdr;
           $item->mdr_total_count = $mdrTotalCount;
           $item->mdr_count = $mdrCount;

           unset($item->create_time);
           return $item;
        });
        return $this->setPaginate($data);
    }

    public function selectPageList(array $params)
    {
        $pageSize = $params['page_size'] ?? MineModel::PAGE_SIZE;
        $query =  $this->model::query()
            ->select(['id','room_name','room_announcement','room_wxid','room_avatar_url','room_member_count','create_time','room_owner_name','room_owner_time']);
        $data =  $this->selectHandleSearch($query, $params)->orderBy('create_time','desc')->paginate((int)$pageSize);
        $data->getCollection()->transform(function ($item){
            $item->create_time_text = datetime($item->create_time);
            unset($item->create_time);
            return $item;
        });
        return $this->setPaginate($data);
    }

    /**
     * 获取群列表
     * @param $params
     * @return Collection|array
     */
    public function getRoomList($params): Collection|array
    {
        $query =  $this->model::query()
            ->select(['id','room_name','room_announcement','room_wxid','room_avatar_url','room_member_count','create_time','room_owner_name','room_owner_time']);
        $data =  $this->selectHandleSearch($query, $params)->orderBy('create_time','desc')->get();
        return $data;
    }

    /**
     * 获取群wxid
     * @param array $params
     * @return \Hyperf\Collection\Collection
     */
    public function getRoomWxids(array $params): \Hyperf\Collection\Collection
    {
        return $this->selectHandleSearch($this->model::query(),$params)->pluck('room_wxid');
    }

    public function getExportRoomList($params): array
    {
        $query =  $this->model::query()
            ->select(['id','room_name','room_member_count','create_time','room_owner_name','room_owner_time','room_is_forbid_change_name','room_is_invite_confirmation'])
            ->with(['robots'=>function ($query) {
                $query->select(['id','nickname']);
            },'roomTags' => function ($query) {
                $query->select(['id','name']);
            }]);
        $data =  $this->selectHandleSearch($query, $params)->orderBy('create_time','desc')->get()->toArray();
        return $data;
    }

    /**
     * 检查是否是群主
     * @param mixed $roomWxid
     * @param mixed $userId
     * @return bool
     */
    public function checkIsRoomOwner(mixed $roomWxid,mixed $userId): bool
    {
        return $this->model::query()
            ->where('room_wxid',$roomWxid)
            ->where('create_user_id',$userId)
            ->exists();
    }

    /**
     * 获取可转群列表
     * @param array $params
     * @return array
     */
    public function getTransferableRooms(array $params): array
    {
        $fromRobotId = $params['from_robot_id'];
        $toRobotUserId = $params['to_robot_user_id'];
        $pageSize = $params['page_size'] ?? MineModel::PAGE_SIZE;
        $query =  $this->model::query()
            ->select(['id','room_name','room_announcement','room_wxid','room_avatar_url','room_member_count','create_time','room_owner_name','room_owner_time'])
            ->whereHas('robots',function ($query) use ($fromRobotId){
                $query->where('robot_id',$fromRobotId);
            })
            ->where('create_user_id',$params['from_robot_user_id'])
            ->whereHas('roomMembers',function ($query) use ($toRobotUserId){
                $query->where('user_id',$toRobotUserId);
            });
        $data =  $this->selectHandleSearch($query, $params)->orderBy('create_time','desc')->paginate((int)$pageSize);
        return $this->setPaginate($data);
    }

    /**
     * 获取可设置管理员的群聊
     * @param array $params
     * @return array
     */
    public function getAdminAssignableRooms(array $params): array
    {
        $fromRobotId = $params['from_robot_id'];
        $toRobotUserId = $params['to_robot_user_id'];
        $pageSize = $params['page_size'] ?? MineModel::PAGE_SIZE;
        $query =  $this->model::query()
            ->select(['id','room_name','room_announcement','room_wxid','room_avatar_url','room_member_count','create_time','room_owner_name','room_owner_time'])
            ->whereHas('robots',function ($query) use ($fromRobotId){
                $query->where('robot_id',$fromRobotId);
            })
            ->where('create_user_id',$params['from_robot_user_id'])
            ->whereHas('roomMembers',function ($query) use ($toRobotUserId){
                $query->where('user_id',$toRobotUserId)->where('is_admin',0);
            })->whereHas('roomMembers',function ($query){
                $query->where('is_admin',1)->havingRaw('count(id) < 3');
            });
        $data =  $this->selectHandleSearch($query, $params)->orderBy('create_time','desc')->paginate((int)$pageSize);
        return $this->setPaginate($data);
    }

    public function handleSearch(Builder $query, array $params): Builder
    {
        $mainUid = $params['main_uid'];
        $uid = $params['uid'];
        $groupId = data_get($params,'group_id',-1);

        $query->where('main_uid',$params['main_uid']);

        if($mainUid != $uid){
            $query->whereHas('robots',function ($query) use ($uid){
                $query->withPermission($uid);
            });
        }

        if(filled($groupId)){
            if($groupId == 0){
                $query->doesntHave('groups');
            }elseif($groupId != -1){
                $query->whereHas('groups',function ($query) use ($groupId){
                    $query->where('group_id',$groupId);
                });
            }
        }
        //群名称查询
        if(!empty($params['room_name'])){
            $roomName = $params['room_name'];
            $includeMatchType = $roomName['include_match_type'] ?? 0;
            $includeKeywords = $roomName['include_keywords'] ?? [];
            $excludeSwitch = $roomName['exclude_switch'] ?? 0;
            $excludeMatchType = $roomName['exclude_match_type'] ?? 0;
            $excludeKeywords = $roomName['exclude_keywords'] ?? [];

            if (!empty($includeKeywords)) {
                if ($includeMatchType == 0) {
                    $query->where(function ($query) use ($includeKeywords){
                        // 含任意一个关键词
                        foreach ($includeKeywords as $keyword) {
                            $query->orWhere('room_name', 'like', "%{$keyword}%");
                        }
                    });
                } else {
                    $query->where(function ($query) use ($includeKeywords){
                        // 含所有关键词
                        foreach ($includeKeywords as $keyword) {
                            $query->where('room_name', 'like', "%{$keyword}%");
                        }
                    });
                }
            }

            // 添加排除条件
            if ($excludeSwitch == 1 && !empty($excludeKeywords)) {
                if ($excludeMatchType == 0) {
                    $query->where(function ($query) use ($excludeKeywords){
                        // 含任意一个排除关键词
                        foreach ($excludeKeywords as $keyword) {
                            $query->where('room_name', 'not like', "%{$keyword}%");
                        }
                    });
                } else {
                    $query->where(function ($query) use ($excludeKeywords){
                        // 含所有排除关键词
                        foreach ($excludeKeywords as $keyword) {
                            $query->where('room_name', 'not like', "%{$keyword}%");
                        }
                    });
                }
            }
        }elseif(!empty($params['keyword'])){
            $query->where(function ($query) use ($params){
                $query->where('room_name','like','%'.$params['keyword'].'%')->orWhere('room_owner_name','like','%'.$params['keyword'].'%');
            });
        }

        if(!empty($params['robot_id'])){
            $robotId = $params['robot_id'];
            $query->whereHas('robots',function ($query) use ($robotId){
                $query->where('robot_id',$robotId);
            });
        }

        if(!empty($params['corp_id'])){
            $corpId = $params['corp_id'];
            $query->whereHas('robots',function ($query) use ($corpId){
                $query->where('corp_id',$corpId);
            });
        }

        //群标签
        $roomTag = data_get($params, 'room_tag');
        if (!empty($roomTag)) {
            $roomTag = !is_array($roomTag) ? json_decode($roomTag,true) : $roomTag;
            $includeMatchType = data_get($roomTag, 'include_match_type', 0);
            $includeTagIds = data_get($roomTag, 'include_tag_ids');

            switch ($includeMatchType) {
                case 0:
                    //满足任意一个标签
                    $query->whereHas('roomTags', function ($query) use ($includeTagIds) {
                        $query->whereIn('tag_id', $includeTagIds);
                    });
                    break;
                case 1:
                    //满足所有标签
                    $query->whereHas('roomTags', function ($query) use ($includeTagIds) {
                        $query->whereIn('tag_id', $includeTagIds);
                    }, '=', count($includeTagIds));
                    break;
                default:
                    //未打标签
                    $query->doesntHave('roomTags');
                    break;
            }

            $excludeSwitch = data_get($roomTag, 'exclude_switch', 0);
            if ($excludeSwitch) {
                $excludeMatchType = data_get($roomTag, 'exclude_match_type', 0);
                $excludeTagIds = data_get($roomTag, 'exclude_tag_ids');

                switch ($excludeMatchType) {
                    case 0:
                        //满足任意一个标签
                        $query->whereDoesntHave('roomTags', function ($query) use ($excludeTagIds) {
                            $query->whereIn('tag_id', $excludeTagIds);
                        });
                        break;
                    case 1:
                        //满足所有标签
                        $query->whereDoesntHave('roomTags', function ($query) use ($excludeTagIds) {
                            $query->whereIn('tag_id', $excludeTagIds)->havingRaw('COUNT(*) = ?', [count($excludeTagIds)]);
                        });
                        break;
                    default:
                        //未打标签
                        $query->doesntHave('roomTags');
                        break;
                }
            }
        }

        //创建时间
        if(!empty($params['create_start_time']) && !empty($params['create_end_time'])){
            $query->whereBetween('create_time', [strtotime($params['create_start_time']), strtotime($params['create_end_time'])]);
        }

        //成为群主时间
        if(!empty($params['room_owner_start_time']) && !empty($params['room_owner_end_time'])){
            $query->whereBetween('room_owner_time', [$params['room_owner_start_time'], $params['room_owner_end_time']]);
        }

        //群数量
        $roomMemberMinCount = data_get($params, 'room_member_min_count');
        $roomMemberMaxCount = data_get($params, 'room_member_max_count');

        if(filled($roomMemberMinCount) && filled($roomMemberMaxCount)){
            $query->whereBetween('room_member_count', [$roomMemberMinCount, $roomMemberMaxCount]);
        }

        //机器人所在群，对应的类型：0=管理员或群主，1=群主，2=成员
        $memberType = data_get($params, 'member_type');
        $robotUserId = data_get($params, 'robot_user_id');
        if(filled($memberType) && !empty($robotUserId)){
            $robotUserId = !is_array($robotUserId) ? json_decode($robotUserId,true) : $robotUserId;
            $query->whereHas('roomMembers', function ($query) use ($memberType,$robotUserId){
                $query->whereIn('user_id',$robotUserId);
                if($memberType == 0){
                    //管理员或群主
                    $query->where(function ($query){
                       $query->where('is_owner',1)->orWhere('is_admin',1);
                    });
                }elseif ($memberType == 1){
                    //群主
                    $query->where('is_owner',1);
                }else{
                    //成员
                    $query->where('is_owner','<>',1)->where('is_admin','<>',1);
                }
            });
        }

        if(isset($params['room_member_config'])){
            $roomMemberConfig = $params['room_member_config'];
            $type = data_get($roomMemberConfig, 'type');
            $roomMemberUserId = data_get($roomMemberConfig, 'user_id');
            if($type == 1){
                $query->whereHas('roomMembers', function ($query) use ($roomMemberUserId){
                    $query->whereNotIn('user_id',$roomMemberUserId);
                });
            }
        }

        return $query;
    }

    public function selectHandleSearch(Builder $query, array $params): Builder
    {
        $mainUid = $params['main_uid'];
        $uid = $params['uid'];
        $query->where('main_uid',$mainUid);
        if($mainUid != $uid){
            $query->whereHas('robots',function ($query) use ($uid){
                $query->withPermission($uid);
            });
        }

        $robotId = data_get($params, 'robot_id');
        if(!empty($robotId)){
            $query->whereHas('robots',function ($query) use ($robotId){
                $query->where('robot_id',$robotId);
            });
        }

        //包含条件
        $includeParams = data_get($params, 'include_params');
        if(!empty($includeParams)){
            $includeParams = !is_array($includeParams)?json_decode($includeParams, true):$includeParams;
            //群名称
            $roomName = data_get($includeParams, 'room_name');
            if(!empty($roomName) && !empty($roomName['keywords'])){
                $includeMatchType = data_get($roomName, 'match_type', 0);
                $includeKeywords = $roomName['keywords'];
                if ($includeMatchType == 0) {
                    $query->where(function ($query) use ($includeKeywords){
                        // 含任意一个关键词
                        foreach ($includeKeywords as $keyword) {
                            $query->orWhere('room_name', 'like', "%{$keyword}%");
                        }
                    });
                } else {
                    $query->where(function ($query) use ($includeKeywords){
                        // 含所有关键词
                        foreach ($includeKeywords as $keyword) {
                            $query->where('room_name', 'like', "%{$keyword}%");
                        }
                    });
                }
            }

            //群标签
            $roomTag = data_get($includeParams, 'room_tag');
            if(!empty($roomTag) && !empty($roomTag['ids'])){
                $includeMatchType = data_get($roomTag, 'match_type', 0);
                $includeTagIds = $roomTag['ids'];
                switch ($includeMatchType) {
                    case 0:
                        //满足任意一个标签
                        $query->whereHas('roomTags', function ($query) use ($includeTagIds) {
                            $query->whereIn('tag_id', $includeTagIds);
                        });
                        break;
                    case 1:
                        //满足所有标签
                        $query->whereHas('roomTags', function ($query) use ($includeTagIds) {
                            $query->whereIn('tag_id', $includeTagIds);
                        }, '=', count($includeTagIds));
                        break;
                    default:
                        //未打标签
                        $query->doesntHave('roomTags');
                        break;
                }
            }

            $robotId = data_get($includeParams, 'robot_id');
            if(!empty($robotId)){
                $query->whereHas('robots',function ($query) use ($robotId){
                    $query->where('robot_id',$robotId);
                });
            }

            $memberType = data_get($includeParams, 'member_type');
            if(filled($memberType) && !empty($robotId)){
                $query->whereHas('roomMembers', function ($query) use ($memberType,$robotId){
                    if($memberType == 0){
                        //管理员或群主
                        $query->join('ch_robot',function ($join) use ($robotId){
                            $join->on( 'ch_room_member.user_id', '=', 'ch_robot.user_id')->where('ch_robot.id',$robotId);
                        })->where('ch_room_member.is_owner',1)->orWhere('ch_room_member.is_admin',1);
                    }elseif ($memberType == 1){
                        //群主
                        $query->join('ch_robot',function ($join) use ($robotId){
                            $join->on( 'ch_room_member.user_id', '=', 'ch_robot.user_id')->where('ch_robot.id',$robotId);
                        })->where('ch_room_member.is_owner',1);
                    }else{
                        //成员
                        $query->join('ch_robot',function ($join) use ($robotId){
                            $join->on( 'ch_room_member.user_id', '=', 'ch_robot.user_id')->where('ch_robot.id',$robotId);
                        })->where('ch_room_member.is_owner','<>',1)->where('ch_room_member.is_admin','<>',1);
                    }
                });
            }

            $roomMemberMinCount = data_get($includeParams, 'min_member_count');
            $roomMemberMaxCount = data_get($includeParams, 'max_member_count');

            if(filled($roomMemberMinCount) && filled($roomMemberMaxCount)){
                $query->whereBetween('room_member_count', [$roomMemberMinCount, $roomMemberMaxCount]);
            }
        }
        //排除条件
        $excludeParams = data_get($params, 'exclude_params');
        if(!empty($excludeParams)){
            $excludeParams = !is_array($excludeParams)?json_decode($excludeParams, true):$excludeParams;
            //群名称
            $roomName = data_get($excludeParams, 'room_name');
            if(!empty($roomName) && !empty($roomName['keywords'])){
                $includeMatchType = data_get($roomName, 'match_type', 0);
                $includeKeywords = $roomName['keywords'];
                if ($includeMatchType == 0) {
                    $where = [];
                    foreach ($includeKeywords as $keyword) {
                        $where[] = " room_name like '%{$keyword}%' ";
                    }
                    $where = implode('or',$where);
                    $query->whereRaw("not ({$where})");
                } else {
                    $query->where(function ($query) use ($includeKeywords){
                        // 含所有关键词
                        foreach ($includeKeywords as $keyword) {
                            $query->where('room_name', 'not like', "%{$keyword}%");
                        }
                    });
                }
            }

            $robotId = data_get($excludeParams, 'robot_id');
            if(!empty($robotId)){
                $query->whereDoesntHave('robots',function ($query) use ($robotId){
                    $query->where('robot_id',$robotId);
                });
            }

            $memberType = data_get($excludeParams, 'member_type');
            if(filled($memberType) && !empty($robotId)){
                $query->whereDoesntHave('roomMembers', function ($query) use ($memberType,$robotId){
                    if($memberType == 0){
                        //管理员或群主
                        $query->join('ch_robot',function ($join) use ($robotId){
                            $join->on( 'ch_room_member.user_id', '=', 'ch_robot.user_id')->where('ch_robot.id',$robotId);
                        })->where('ch_room_member.is_owner',1)->orWhere('ch_room_member.is_admin',1);
                    }elseif ($memberType == 1){
                        //群主
                        $query->join('ch_robot',function ($join) use ($robotId){
                            $join->on( 'ch_room_member.user_id', '=', 'ch_robot.user_id')->where('ch_robot.id',$robotId);
                        })->where('ch_room_member.is_owner',1);
                    }else{
                        //成员
                        $query->join('ch_robot',function ($join) use ($robotId){
                            $join->on( 'ch_room_member.user_id', '=', 'ch_robot.user_id')->where('ch_robot.id',$robotId);
                        })->where('ch_room_member.is_owner','<>',1)->where('ch_room_member.is_admin','<>',1);
                    }
                });
            }

            $roomMemberMinCount = data_get($excludeParams, 'min_member_count');
            $roomMemberMaxCount = data_get($excludeParams, 'max_member_count');
            if(filled($roomMemberMinCount) && filled($roomMemberMaxCount)){
                $query->whereNotBetween('room_member_count', [$roomMemberMinCount, $roomMemberMaxCount]);
            }
        }
        return $query;
    }
}